package com.oig.sys.auth.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.oig.common.constants.CommonEnum;
import com.oig.common.exception.BadRequestException;
import com.oig.common.module.SysPageInfo;
import com.oig.common.module.SysRequest;
import com.oig.sys.auth.constants.AuthConstants;
import com.oig.sys.auth.dao.ITbSystemDao;
import com.oig.sys.auth.dao.ITbUserDao;
import com.oig.sys.auth.dto.RoleDto;
import com.oig.sys.auth.dto.UserDto;
import com.oig.sys.auth.entity.TbSystem;
import com.oig.sys.auth.entity.TbUser;
import com.oig.sys.auth.service.ITbDepartUserService;
import com.oig.sys.auth.service.ITbJobRoleService;
import com.oig.sys.auth.service.ITbResourceColumnService;
import com.oig.sys.auth.service.ITbResourceQltmpService;
import com.oig.sys.auth.service.ITbTenantInfoService;
import com.oig.sys.auth.service.ITbUserColumnService;
import com.oig.sys.auth.service.ITbUserJobService;
import com.oig.sys.auth.service.ITbUserRoleService;
import com.oig.sys.auth.service.ITbUserService;
import com.oig.sys.auth.vo.TenantInfoVo;
import com.oig.sys.auth.vo.UserVo;
import com.oig.sys.security.util.SecurityUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

/**
 * @author wuxl
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class TbUserServiceImpl implements ITbUserService {

	private final ITbUserDao tbUserDao;
	private final ITbUserRoleService userRoleService;
	private final ITbDepartUserService departUserService;
	private final ITbUserJobService userJobService;
	private final ITbJobRoleService jobRoleService ;
	private final ITbSystemDao systemDao;
	private final ITbTenantInfoService tenantInfoService;
	private final ITbUserColumnService userColumnService ;
	private final ITbResourceColumnService resourceColumnService;
	private final ITbResourceQltmpService resourceQltmpService;



	@Override
	public UserVo getUserInfo(Integer userId) {
		UserVo userVo = this.getUserById(userId);
		if (userVo==null){
			throw new BadRequestException("用户不存在");
		}
		userVo.setDepartmentList(this.departUserService.getUserDepart(userVo.getId()));
		userVo.setJobList(this.userJobService.getUserJob(userVo.getId()));
		userVo.setRoleList(this.userRoleService.getUserRole(userVo.getId()));
		return userVo;
	}

	private String buildCode(Integer tenantId){
	    int index = 1 ;
        String codeIndex = this.tbUserDao.findMaxCode(tenantId);
        if (StringUtils.isNotBlank(codeIndex)){
            try{
                log.debug("codeIndex:{}-{}",codeIndex, tenantId);
                index = Integer.parseInt(codeIndex) + 1;
            } catch (Exception e){
                throw new BadRequestException("请填写工号") ;
            }
        }
        return StringUtils.leftPad(String.valueOf(index),6,"0");
    }

	@Transactional
	@Override
	public UserVo addUser(UserDto userDto) {
		if (userDto.getTenantId()==0){
			userDto.setTenantCode(SecurityUtil.getDefineUserMain().getTenantCode());
			userDto.setTenantName(SecurityUtil.getDefineUserMain().getTenantName());
		} else if (StringUtils.isBlank(userDto.getTenantName())){
			TenantInfoVo tenantInfoVo = this.tenantInfoService.info(userDto.getTenantId());
			log.debug("tenant :{}--{}", tenantInfoVo.getCode(), tenantInfoVo.getName());
			userDto.setTenantCode(tenantInfoVo.getCode());
			userDto.setTenantName(tenantInfoVo.getName());
		}
        if (StringUtils.isBlank(userDto.getPassword())){
        	userDto.setPassword(AuthConstants.DEF_PWD);	//默认密码先写死
		}
		TbUser user = this.tbUserDao.getByCodeOrUsername(userDto.getUsername()) ;
		if (user!=null){
			throw new BadRequestException("用户名重复") ;
		}
		user = new TbUser();
		BeanUtils.copyProperties(userDto, user, "id");
		if (StringUtils.isEmpty(user.getCode())){
		    user.setCode(this.buildCode(user.getTenantId()));
        }
		user.setCreateTime(new Date());
		user.setCreateUserId(SecurityUtil.getDefineUserMain().getId());
		user.setCreateUserName(SecurityUtil.getDefineUserMain().getRealName());
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
		user.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword()));
		user.setExpDate(new Date());
        user.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
        user.setNoDelFlag(CommonEnum.FlagEnum.TRUE_FLAG.getCode());
		this.tbUserDao.save(user) ;
		UserVo userVo = new UserVo();
		BeanUtils.copyProperties(user,userVo,"password");
		if (userDto.getDepartmentList()!=null && !userDto.getDepartmentList().isEmpty()){
			userVo.setDepartmentList(departUserService.addUserDepart(user, userDto.getDepartmentList()));
		}
		if (userDto.getJobList()!=null && !userDto.getJobList().isEmpty()){
			userVo.setJobList(userJobService.addUserJob(user,userDto.getJobList()));
			//查询job的角色，配置到用户
			List<Integer> jobRoleIdList = this.jobRoleService.findAllRoleIdByJob(userDto.getJobList()) ;
			userDto.setRoleList(this.mergeRole(jobRoleIdList, userDto.getRoleList()));
		}
		if (userDto.getRoleList()!=null && !userDto.getRoleList().isEmpty()){
			userVo.setRoleList(userRoleService.addUserRole(user, userDto.getRoleList()));
		}
		return userVo;
	}



	private List<RoleDto> mergeRole(List<Integer> jobRoleIdList, List<RoleDto> roleDtoList){
		if (jobRoleIdList==null||jobRoleIdList.isEmpty()){
			return roleDtoList;
		}
		List<RoleDto> jobRoleDtoList = new ArrayList<>() ;
		if (roleDtoList!=null){
			for (RoleDto roleDto : roleDtoList){
				for (Iterator<Integer> iter = jobRoleIdList.iterator(); iter.hasNext();){
					Integer jobRoleId = iter.next();
					if (roleDto.getId().equals(jobRoleId)){
						iter.remove();
						break;
					}
				}
			}
			jobRoleDtoList.addAll(roleDtoList);
		}
		for (Integer jobRoleId : jobRoleIdList){
			RoleDto roleDto = new RoleDto();
			roleDto.setId(jobRoleId);
			jobRoleDtoList.add(roleDto);
		}
		return jobRoleDtoList;
	}

	@Transactional
	@Override
	public UserVo updateUser(UserDto userDto) {
		TbUser user = this.tbUserDao.getTbUserById(userDto.getId()) ;
		log.debug("user entity:{}", user);
		BeanUtils.copyProperties(userDto, user, "id","code","username","password",
				"defaultSysId","defaultSysName","feishuUserId","relationId","status","tenantId","tenantCode","tenantName");
		if (StringUtils.isNotBlank(userDto.getFeishuUserId())){
			user.setFeishuUserId(userDto.getFeishuUserId());
      	}
		if (StringUtils.isNotBlank(userDto.getPassword())){
			BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
			user.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword()));
		}
		if (userDto.getDefaultSysId()!=null){
			user.setDefaultSysId(userDto.getDefaultSysId());
			user.setDefaultSysName(userDto.getDefaultSysName());
		}
		//user.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		user.setLastUpdateName(SecurityUtil.getDefineUserMain().getRealName());
		user.setLastUpdateId(SecurityUtil.getDefineUserMain().getId());
		user.setLastUpdateTime(new Date());
		this.tbUserDao.save(user) ;
		UserVo userVo = new UserVo();
		BeanUtils.copyProperties(user, userVo, "password");
		if (userDto.getDepartmentList()!=null && !userDto.getDepartmentList().isEmpty()){
			userVo.setDepartmentList(departUserService.updateUserDepart(user, userDto.getDepartmentList()));
		}
		if (userDto.getJobList()!=null && !userDto.getJobList().isEmpty()){
			userVo.setJobList(userJobService.updateUserJob(user,userDto.getJobList()));
			//查询job的角色，配置到用户
			List<Integer> jobRoleIdList = this.jobRoleService.findAllRoleIdByJob(userDto.getJobList()) ;
			userDto.setRoleList(this.mergeRole(jobRoleIdList, userDto.getRoleList()));
		}
		if (userDto.getRoleList()!=null && !userDto.getRoleList().isEmpty()){
			userVo.setRoleList(userRoleService.updateUserRole(user, userDto.getRoleList()));
		}
		return userVo;
	}


	@Transactional
	@Override
	public void setDefaultSystem(Integer systemId, String systemName) {
		TbUser user = this.tbUserDao.getTbUserById(SecurityUtil.getDefineUserMain().getId()) ;
		TbSystem system = this.systemDao.getTbSystemById(systemId) ;
		user.setDefaultSysId(system.getId());
		user.setDefaultSysName(system.getSystemName());
		user.setDefaultSysImgUrl(system.getImageUrl());
		this.tbUserDao.save(user);
	}

	@Transactional
	@Override
	public void updateUserDefSystemName(Integer systemId, String systemName, String systemImageUrl) {
		List<Integer> userIdList = this.tbUserDao.findAllIdByDefSystemName(systemId);
		if (userIdList!=null&&!userIdList.isEmpty()){
			this.tbUserDao.updateDefSystemName(userIdList, systemName, systemImageUrl);
		}
	}

	@Transactional
	@Override
	public void cleanUserDefSystem(Integer systemId) {
		List<Integer> userIdList = this.tbUserDao.findAllIdByDefSystemName(systemId);
		if (userIdList!=null&&!userIdList.isEmpty()){
			this.tbUserDao.updateDefSystem(userIdList, null, null, null);
		}
	}


	@Transactional
	@Override
	public void updatePwd(UserDto userDto) {
		TbUser user = this.tbUserDao.getTbUserById(SecurityUtil.getDefineUserMain().getId()) ;
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
		//String oldPwd = bCryptPasswordEncoder.encode(userDto.getOldPassword()) ;
		if (!bCryptPasswordEncoder.matches(userDto.getOldPassword(), user.getPassword())){
			throw new BadRequestException("旧密码错误") ;
		}
		user.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword()));
		user.setExpDate(DateUtil.offsetDay(new Date(), 90));
		this.tbUserDao.save(user);
	}

    @Transactional
    @Override
    public void resetPwd(Integer userId) {
        TbUser user = this.tbUserDao.getTbUserById(userId) ;
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        user.setPassword(bCryptPasswordEncoder.encode(AuthConstants.DEF_PWD));
        user.setExpDate(DateUtil.offsetDay(new Date(), 90));
        this.tbUserDao.save(user) ;
    }

    @Transactional
	@Override
	public void cancelUser(Integer userId) {
		TbUser user = this.tbUserDao.getTbUserById(userId) ;
		if (user.getId().equals(SecurityUtil.getDefineUserMain().getId())){
			throw new BadRequestException("不允许删除自己") ;
		}
		if (CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(user.getNoDelFlag())){
			throw new BadRequestException("用户不允许删除") ;
		}
		user.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
		this.tbUserDao.save(user) ;
	}

	@Transactional
	@Override
	public void revertUser(Integer userId) {
		TbUser user = this.tbUserDao.getTbUserById(userId) ;
		if (!CommonEnum.CommonStatusEnum.DEL.getCode().equals(user.getStatus())){
            throw new BadRequestException("用户非注销状态") ;
        }
		user.setStatus(CommonEnum.CommonStatusEnum.OK.getCode());
		this.tbUserDao.save(user) ;
	}

	@Transactional
	@Override
	public void delUser(Integer userId) {
		TbUser user = this.tbUserDao.getTbUserById(userId) ;
		if (!CommonEnum.CommonStatusEnum.DEL.getCode().equals(user.getStatus())){
			throw new BadRequestException("只有注销状态才能清除数据") ;
		}
		//user depart
		this.departUserService.delDepartUser(null, userId);
		//user job
		this.userJobService.delByUserId(userId);
		//user role
		this.userRoleService.delUserRoleByUserId(userId);
		//user column
		List<Integer> resourceIdList = this.userColumnService.findAllUCResourceIdByUserId(userId);
		this.resourceColumnService.delAllColumnByResourceIdList(resourceIdList);
		//user qltmp
		this.resourceQltmpService.delUserQltByUserId(userId);
		//user open_id
		this.tbUserDao.delete(user);
	}


	@Transactional
	@Override
	public void cancelUserNotInListId(List<Integer> userIdList, Integer tenantId) {
		List<TbUser> userList = this.tbUserDao.findTenantNeedCancelUser(userIdList, tenantId);
		if (userList!=null && !userList.isEmpty()){
			userList.forEach(user -> {
				log.debug("need cancel user:{}", user.getId());
				if (CommonEnum.FlagEnum.FALSE_FLAG.getCode().equals(user.getNoDelFlag())){
					return;
				}
				log.debug("cancel user:{}", user);
				user.setStatus(CommonEnum.CommonStatusEnum.DEL.getCode());
				this.tbUserDao.save(user) ;
			});
		}
	}



	@Override
	public List<UserVo> listAllTenantUser(Integer tenantId) {
		List<TbUser> userList = this.tbUserDao.findAllByTenantId(tenantId);
		List<UserVo> userVoList = new ArrayList<>();
		userList.forEach(item->userVoList.add(this.buildRtVo(item)));
		return userVoList;
	}

	@Override
	public List<UserVo> listAllDeptUser(Integer deptId) {
		return departUserService.getDepartUser(deptId);
	}

	@Override
	public SysPageInfo<UserVo> listAllUser(SysRequest<UserDto> userRequest) {
		Pageable pageable = userRequest.pageable();
		Specification<TbUser> specification = new Specification<TbUser>(){
			@Override
			@Nullable
			public Predicate toPredicate(@NonNull Root<TbUser> root, @NonNull CriteriaQuery<?> criteriaQuery, @NonNull CriteriaBuilder criteriaBuilder) {
				List<Predicate> predicateList = new ArrayList<>();
				Path<Integer>  tenantIdPath = root.get("tenantId");
				predicateList.add(criteriaBuilder.equal(tenantIdPath, userRequest.getParamDto().getTenantId()));

				if (StrUtil.isNotBlank(userRequest.getParamDto().getStatus())){
					Path<String> statusPath = root.get("status");
					predicateList.add(criteriaBuilder.equal(statusPath, userRequest.getParamDto().getStatus()));
				} else {
					Path<String> statusPath = root.get("status");
					predicateList.add(criteriaBuilder.equal(statusPath, CommonEnum.CommonStatusEnum.OK.getCode()));
				}
				if (StrUtil.isNotBlank(userRequest.getParamDto().getUsername())){
					Path<String> usernamePath = root.get("username");
					predicateList.add(criteriaBuilder.like(usernamePath, "%"+userRequest.getParamDto().getUsername()+"%"));
				}
				if (StrUtil.isNotBlank(userRequest.getParamDto().getNickName())){
					Path<String> nickNamePath = root.get("nickName");
					predicateList.add(criteriaBuilder.like(nickNamePath, "%"+userRequest.getParamDto().getNickName()+"%"));
				}
				if (StrUtil.isNotBlank(userRequest.getParamDto().getRealName())){
					Path<String> realNamePath = root.get("realName");
					predicateList.add(criteriaBuilder.like(realNamePath, "%"+userRequest.getParamDto().getRealName()+"%"));
				}
				if (StrUtil.isNotBlank(userRequest.getParamDto().getPhone())){
					Path<String> phonePath = root.get("phone");
					predicateList.add(criteriaBuilder.like(phonePath, "%"+userRequest.getParamDto().getPhone()+"%"));
				}
				criteriaQuery.where(predicateList.toArray(new Predicate[0]));
				return criteriaQuery.getRestriction();
			}
		};
		Page<TbUser> page  = this.tbUserDao.findAll(specification, pageable);
		List<UserVo> userVoList = new ArrayList<>();
		for (TbUser user : page.getContent()){
			UserVo userVo = new UserVo();
			BeanUtils.copyProperties(user,userVo);
			userVoList.add(userVo);
		}
		SysPageInfo<UserVo> sysPageInfo = new SysPageInfo<>(userRequest.getPageNo(), userRequest.getPageSize(), page.getTotalElements());
		sysPageInfo.setList(userVoList);
		return sysPageInfo;
	}

	@Override
	public UserVo loadUserByCodeOrUsername(String loadCode) {
		TbUser user = this.tbUserDao.getByCodeOrUsername(loadCode) ;
		return buildRtVo(user);
	}

	private UserVo buildRtVo(TbUser user){
		if (user==null){
			return null ;
		}
		UserVo userVo = new UserVo();
		BeanUtils.copyProperties(user, userVo);
		return userVo;
	}

	@Override
	public UserVo getUserById(Integer userId) {
		TbUser user = this.tbUserDao.getTbUserById(userId);
		return this.buildRtVo(user);
	}

	@Override
	public UserVo getUserByCode(String code) {
		TbUser user = this.tbUserDao.getUserByCode(code);
		return this.buildRtVo(user);
	}



    @Transactional
	@Override
	public void updateLoginSucess(UserDto userDto) {
		TbUser user = this.tbUserDao.getTbUserById(userDto.getId()) ;
		if (user!=null){
			user.setLastLoginIp(userDto.getLastLoginIp());
			user.setLastLoginTime(new Date());
			user.setPwdFailNum(0);
			user.setPwdFailTime(null);
			this.tbUserDao.save(user);
		}
	}

	@Transactional
	@Override
	public void updateLoginFail(UserDto userDto) {
		TbUser user = this.tbUserDao.getByCodeOrUsername(userDto.getUsername());
		if (user!=null){
			if (user.getPwdFailNum()==null){
				user.setPwdFailNum(1);
			} else {
				user.setPwdFailNum(user.getPwdFailNum()+1);
			}
			user.setPwdFailTime(new Date());
			this.tbUserDao.save(user);
		}
	}


	@Transactional
	@Override
	public UserVo addOrUpdateUser(UserDto userDto) {
		if (StringUtils.isNotBlank(userDto.getFeishuUserId())){		//有飞书id优先按飞书openId去查一下，
			TbUser user = this.tbUserDao.getTbUserByFeiShuId(userDto.getFeishuUserId());
			if (user == null && StringUtils.isNotBlank(userDto.getCode())){	//没有再按工号查一下
				user = this.tbUserDao.getUserByCode(userDto.getCode());
			}
			if (user!=null) {
				userDto.setId(user.getId());
			}
		}
        if (userDto.getId()==null){
			TbUser oldUser = this.tbUserDao.getByUsername(userDto.getUsername()) ;
			if (oldUser!=null){
				//英文名相同
				userDto.setUsername("X"+userDto.getCode());
			}
			log.debug("sync fs add user :{}", userDto);
            return this.addUser(userDto);
        } else {
			log.debug("sync fs update user :{}", userDto);
        	return this.updateUser(userDto);
        }
	}

	@Override
	public List<UserVo> listUserByUserIdList(List<Integer> userIdList) {
		List<TbUser> userList = tbUserDao.findAllByIds(userIdList);
		List<UserVo> userVoList = new ArrayList<>();
		userList.forEach(item->userVoList.add(this.buildRtVo(item)));
		return userVoList;
	}

	@Override
	public List<UserVo> listUserByUserCodeList(List<String> userCodeList) {
		List<TbUser> userList = this.tbUserDao.findUserByCodeList(userCodeList);
		List<UserVo> userVoList = new ArrayList<>();
		if (userList!=null && !userList.isEmpty()){
			userList.forEach(user -> {
				UserVo userVo = new UserVo();
				BeanUtils.copyProperties(user,userVo);
				userVoList.add(userVo);
			});
		}
		return userVoList;
	}
}
